package com.ccys.doctor.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.ccys.common.constants.CoreAppConstants;
import com.ccys.common.util.Kit;
import com.ccys.common.vo.ResponseResultApp;
import com.ccys.db.cache.SysCodeUtil;
import com.ccys.db.cache.UserUtil;
import com.ccys.db.dto.doctor.cancelConsult.CancelBookingAppointmentDto;
import com.ccys.db.dto.doctor.cancelConsult.CancelConsultationDto;
import com.ccys.db.dto.doctor.cancelConsult.CancelOfflineConsultationDto;
import com.ccys.db.dto.doctor.cancelConsult.SkipConsultationDto;
import com.ccys.db.entity.*;
import com.ccys.db.mapper.*;
import com.ccys.db.vo.doctor.patientHistory.BookingInfoVo;
import com.ccys.doctor.service.CancelConsultService;
import com.ccys.doctor.service.DoctorConsultStageService;
import com.ccys.doctor.service.PatientHistoryService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Date;
import java.util.List;

/**
 * @program: WhiteCoat
 * @description: 取消咨询相关接口
 * @author: ZhangJingCan
 * @create: 2024-02-05 13:15
 **/

@Service
@Slf4j
public class CancelConsultServiceImpl implements CancelConsultService {

    @Resource
    private TblBookingMapper tblBookingMapper;

    @Autowired
    private DoctorConsultStageService doctorConsultStageService;

    @Resource
    private TblBookingLogMapper tblBookingLogMapper;

    @Resource
    private TblBookingFeeMapper tblBookingFeeMapper;

    @Resource
    private TblBookingSummaryMapper tblBookingSummaryMapper;

    @Resource
    private TblBookingReferralLetterMapper tblBookingReferralLetterMapper;

    @Resource
    private TblBookingCancellationReasonMapper tblBookingCancellationReasonMapper;

    @Resource
    private TblBookingSymptomMapper tblBookingSymptomMapper;

    @Resource
    private TblSymptomMapper tblSymptomMapper;

    @Resource
    private TblDiagnosisMapper tblDiagnosisMapper;

    @Resource
    private TblBookingDiagnosisMapper tblBookingDiagnosisMapper;

    @Resource
    private TblBookingDiagnosisLogMapper tblBookingDiagnosisLogMapper;

    @Resource
    private TblBookingConsultationMapper tblBookingConsultationMapper;

    @Resource
    private TblBookingQueueMapper tblBookingQueueMapper;

    @Resource
    private TblSpecialisationMapper tblSpecialisationMapper;

    @Resource
    private TblBookingPackageMapper tblBookingPackageMapper;

    @Resource
    private TblBookingPrescriptionMapper tblBookingPrescriptionMapper;

    @Resource
    private TblDoctorMapper tblDoctorMapper;

    @Resource
    private TblPushQueueChangeMapper tblPushQueueChangeMapper;

    @Autowired
    private PatientHistoryService patientHistoryService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp cancelConsultation(CancelConsultationDto dto) throws ParseException {
        //基础校验
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }

        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorised request.");
        }

        //CASE: Patient cancel before Doctor skip --> Success
        if (-1 == booking.getStatus() && 0 == booking.getSubStatus()) {
            return ResponseResultApp.success();
        } else if (1 == booking.getStatus() || 2 == booking.getStatus() || 3 == booking.getStatus()) {
            return ResponseResultApp.error(doctorConsultStageService.getErrorBookingValid((booking.getStatus())));
        } else if (3 == booking.getStatus() && 0 != booking.getSubStatus()) {
            return ResponseResultApp.error("Sorry, this call ended.");
        }
        Date bLLogDate = tblBookingLogMapper.findOneBlLogDateByBookingId(booking.getId());
        SysCode timeOutCancelBooking = SysCodeUtil.getCode("Time_Out_Cancel_Booking", "2");
        Double aDouble = ObjectUtil.isNotEmpty(timeOutCancelBooking.getValue()) ? Double.valueOf(timeOutCancelBooking.getValue()) : Double.valueOf(0);
        Date calculatedTime = new Date(bLLogDate.getTime() + ((long) (aDouble * 60) * 1000));

        if (calculatedTime.after(new Date())) {
            return ResponseResultApp.error("Please try again after " + Kit.formatDate(bLLogDate, "yyyy-MM-dd HH:mm:ss") + " minute(s) !");
        }
        BigDecimal zero = new BigDecimal(0);
        String bookingId = booking.getId();

        SysCode timeOutConsulation = SysCodeUtil.getCode("Time_Out_Cosulation", "2");
        TblBookingFee bookingFee = tblBookingFeeMapper.findByBookingId(bookingId);
        TblBookingSummary bookingSummary = tblBookingSummaryMapper.findByBookingId(bookingId);
        bookingFee.setStandardFee(zero);
        bookingFee.setDeliveryFee(zero);
        bookingFee.setPrescriptionFee(zero);
        bookingFee.setConsultationFee(zero);
        bookingFee.setTotalAmount(zero);
        booking.setAppointmentStatus(booking.getIsAppointment() == 1 ? 3 : booking.getAppointmentStatus() != null ? booking.getAppointmentStatus() : null);
        bookingSummary.setPresentingComplaint(dto.getPresenting_complaint() != null ? dto.getPresenting_complaint() : null);
        bookingSummary.setManagement(dto.getMamagement() != null ? dto.getMamagement() : null);
        bookingSummary.setInstructionsToPatient(dto.getInstructions_to_patient() != null ? dto.getInstructions_to_patient() : null);
        /**这俩字段不存在 - 未操作
         * booking_reason = @PRM_reason,
         * booking_reason_id = @PRM_reason_id,
         */
        bookingSummary.setPatientWeight(dto.getPatient_weight() != null ? dto.getPatient_weight() : null);
        bookingSummary.setPatientHeight(dto.getPatient_height() != null ? dto.getPatient_height() : null);
        bookingSummary.setPatientTemp(dto.getPatient_temp() != null ? dto.getPatient_temp() : null);
        bookingSummary.setPatientBp1(dto.getPatient_bp1() != null ? dto.getPatient_bp1() : null);
        bookingSummary.setPatientBp2(dto.getPatient_bp2() != null ? dto.getPatient_bp2() : null);
        bookingSummary.setPatientHr(dto.getPatient_hr() != null ? dto.getPatient_hr() : null);

        List<TblBookingReferralLetter> bookingReferralLetters = tblBookingReferralLetterMapper.findByBookingId(bookingId);
        bookingReferralLetters.forEach(item -> {
            if (ObjectUtil.isNotEmpty(bookingSummary.getNeedMedicalLetter())) {
                if (ObjectUtil.isNotEmpty(dto.getMedical_letter())) {
                    item.setReferralLetterCode("ML" + booking.getConsultationNo());
                }
                item.setReferralLetterText(dto.getMedical_letter() != null ? dto.getMedical_letter() : null);
                item.setSpecialistId(dto.getSpecialist_id() != null ? dto.getSpecialist_id() : null);
                tblBookingReferralLetterMapper.updateByPrimaryKeySelective(item);
            }
        });
        if (ObjectUtil.isNotEmpty(bookingSummary.getNeedMedicalCertification())) {
            if (ObjectUtil.isNotEmpty(dto.getMedical_certification_begin_date()) && ObjectUtil.isNotEmpty(dto.getMedical_certification_end_date())) {
                if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")
                        && !Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                    bookingSummary.setMedicalCertificationCode("MC" + booking.getConsultationNo());
                    bookingSummary.setAdditionalNote(dto.getMedical_certification_additional_note() != null ?
                            dto.getMedical_certification_additional_note() : null);
                }
            }
            if (ObjectUtil.isNotEmpty(dto.getMedical_certification_begin_date())) {
                if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                    bookingSummary.setMedicalCertificationBeginDate(dto.getMedical_certification_begin_date());
                }
            }
            if (ObjectUtil.isNotEmpty(dto.getMedical_certification_end_date())) {
                if (!Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                    bookingSummary.setMedicalCertificationEndDate(dto.getMedical_certification_end_date());
                }
            }
        }

        booking.setDoctorManual(dto.getDoctor_manual() != null ? dto.getDoctor_manual() ? 1 : 0 : null);
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        tblBookingSummaryMapper.updateByPrimaryKeySelective(bookingSummary);
        tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);

        //WC-5527: CancellationReason
        APIBookingUpdateCancellationReason(doctor.getId(), bookingId,
                dto.getCancellation_reason() != null ? dto.getCancellation_reason() : null,
                dto.getCancellation_reason_detail() != null ? dto.getCancellation_reason_detail() : null, 2);

        //Symptom
        //函数：API_UpdateBookingSymptom
        tblBookingSymptomMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getSymptoms())) {
            Integer order = tblSymptomMapper.findOrderByGroupId(doctor.getGroupId());
            for (CancelConsultationDto.Symptoms dtoSymptom : dto.getSymptoms()) {
                TblSymptom byNameAndByGroupId = tblSymptomMapper.findByNameAndByGroupId(dtoSymptom.getName(), doctor.getGroupId());
                if (ObjectUtil.isEmpty(byNameAndByGroupId)) {
                    TblSymptom symptom = new TblSymptom();
                    order++;
                    symptom.setDispOrder(order);
                    symptom.setName(dtoSymptom.getName());
                    symptom.setGroupId(doctor.getGroupId());
                    tblSymptomMapper.insertSelective(symptom);
                }
                TblSymptom db = tblSymptomMapper.findByNameAndByGroupId(dtoSymptom.getName(), doctor.getGroupId());
                if (ObjectUtil.isNotEmpty(db)) {
                    TblBookingSymptom bookingSymptom = new TblBookingSymptom();
                    bookingSymptom.setBookingId(booking.getId());
                    bookingSymptom.setSymptomId(db.getId());
                    tblBookingSymptomMapper.insertSelective(bookingSymptom);
                }
            }
        }

        //Diagnosis 函数 API_DoctorUpdateDiagnosis
        APIDoctorUpdateDiagnosis(bookingId, dto.getDiagnosis(), doctor.getCountryOfResidence(), doctor.getGroupId());

        //fix missing end time
        TblBookingConsultation bookingConsultation = tblBookingConsultationMapper.findByBookingId(bookingId);
        bookingConsultation.setEndTime(new Date());
        bookingConsultation.setEndTimeUtc(Kit.getUtcDate());
        tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);

        //Booking status
        if (ObjectUtil.isNotEmpty(booking.getDoctorManual()) && 1 == booking.getDoctorManual()) {
            booking.setStatus(-6);
            booking.setSubStatus(0);
            tblBookingMapper.updateByPrimaryKeySelective(booking);
        } else if (2 == booking.getStatus() && 0 == booking.getSubStatus()) {
            booking.setStatus(-1);
            booking.setSubStatus(-2);
            tblBookingMapper.updateByPrimaryKeySelective(booking);
        } else {
            booking.setStatus(-1);
            booking.setSubStatus(-1);
            tblBookingMapper.updateByPrimaryKeySelective(booking);
        }
        /**
         * todo 上方三个if都需要做：
         * Select TOP(1) @driver_id = driver_id
         * 	From tbl_Booking WITH(NOLOCK)
         * 	Where booking_id = @PRM_booking_id
         *
         * 	Exec [dbo].[API_Insert_Booking_Log] @PRM_booking_id, @PRM_booking_status, @PRM_booking_sub_status, @driver_id
         */

        TblSpecialisation specialisation = tblSpecialisationMapper.selectByPrimaryKey(booking.getSpecialisationId());
        //Queue
        tblBookingQueueMapper.deleteByPrimaryKey(booking.getId());
        tblBookingQueueMapper.updateQueueNoByGroupIdAndByCountryIdAndByIdentification(doctor.getGroupId(), doctor.getCountryOfResidence(), specialisation.getIdentification());
        if (CoreAppConstants.IS_BOOKING_QUEUE_ENABLED == 0) {
            if (ObjectUtil.isNotEmpty(dto.getDoctor_manual()) && dto.getDoctor_manual()) {
                doctorConsultStageService.UpdateQueueNoAfterConnected(booking, -7);
            } else {
                doctorConsultStageService.UpdateQueueNoAfterConnected(booking, -5);
            }
        }

        //Inventory
        DISRecalculateInventoryAfterConsult(bookingId);

        //Delete repeat
        if (2 == booking.getStatus()) {
            tblBookingPackageMapper.updateDelFlagTrueByBookingId(bookingId);
            tblBookingPrescriptionMapper.updateDelFlagTrueByBookingId(bookingId);
        }
        String errorStr = "";
        //Return result - 对方示例给的doctor_manual = false 所以这个是不会进入的
        if (ObjectUtil.isNotEmpty(dto.getDoctor_manual()) && dto.getDoctor_manual()) {
            errorStr = "Your consultation has been disconnected.";
            //todo 这里还有一句,但是感觉没啥用，直接取booking的code不行吗？：string booking_code = until_bl.generateCodeWithEncrypt("booking_code", "tbl_Booking", " And Day(booking_created_on) = Day([dbo].[fn_Add_Hours]()) And Month(booking_created_on) = Month([dbo].[fn_Add_Hours]()) And Year(booking_created_on) = Year([dbo].[fn_Add_Hours]())", Constants.EncryptionKeys.BOOKING, Constants.LENGTH_BOOKING_CODE);
            /**
             * DataTable symptom_photo_dt = new DataTable();
             *                     symptom_photo_dt.Columns.Add("photo", typeof(string));
             *                     if (model.symptom_photos != null)
             *                     {
             *                         var member_code = dsResult.Tables[0].Rows[0]["mem_code"].ToString();
             *                         string folder_code = Constants.S3_Folder.MEMBER + "/" + member_code + "/" + booking_code;
             *                         int i = 1;
             *                         foreach (var symptom_photo in model.symptom_photos)
             *                         {
             *                             if (symptom_photo.photo != null)
             *                             {
             *                                 string str_photo = Utilities.generateRandomImageName("symptom", Constants.LENGTH_RANDOM_IMAGE_NAME, 0, Constants.RandomValueType.INT);
             *                                 amazonUploader.uploadFile(symptom_photo.photo, folder_code, str_photo);
             *                                 symptom_photo_dt.Rows.Add(str_photo);
             *                                 i = i + 1;
             *                             }
             *                         }
             *                     }
             *
             *                     try
             *                     {
             *                         dynamic objResult = connection.Query<dynamic>
             *                         (
             *                             "API_DoctorCreateBookingWhenDisconnected",
             *                             new
             *                             {
             *                                 PRM_booking_code = booking_code,
             *                                 PRM_booking_id = model.booking_id,
             *                                 PRM_symptom_photos = symptom_photo_dt
             *                             },
             *                             transaction,
             *                             commandType: CommandType.StoredProcedure
             *                         ).Single();
             *
             *                         transaction.Commit();
             *                         connection.Close();
             *
             *                         await sqsHelper.UpdateQueueNoAfterConnectedAsync(new UpdateQueueNoAfterConnectedMessage
             *                         {
             *                             BookingId = model.booking_id,
             *                             DeleteFrom = (int)BookingQueueMessageDeleteFrom.DoctorCancelBookingIsManual
             *                         });
             *                     }
             *                     catch (Exception ex)
             *                     {
             *                         transaction.Rollback();
             *                         logger.Error("Disconnected booking: " + ex.ToString());
             *                     }
             *                     finally
             *                     {
             *                         connection.Close();
             *                     }
             */
        } else {
            errorStr = "Your consultation has been cancelled.";
            /**
             * 这里还有一句：
             * await sqsHelper.UpdateQueueNoAfterConnectedAsync(new UpdateQueueNoAfterConnectedMessage
             *                     {
             *                         BookingId = model.booking_id,
             *                         DeleteFrom = (int)BookingQueueMessageDeleteFrom.DoctorCancelBookingIsNotManual
             *                     });
             *
             *  但是代码跟进去有个if，会直接return
             */

            /**
             * todo 还有一个开线程的
             *  #region Push Notification And Broadcast Socket IO For Patients
             *                 Thread thread;
             *                 thread = new Thread(delegate ()
             *                 {
             *                     BroadcastToCMS_ActiveConsultPage();
             *
             *                     #region Broadcast Socket IO
             *                     if (dsResult.Tables.Count > 1)
             *                     {
             *                         DataTable dt = dsResult.Tables[2];
             *                         dt.Columns.Remove(dt.Columns["Mem_ID"]);
             *                         if (model.doctor_manual)
             *                         {
             *                             dsResult.Tables[2].Rows.Add(model.booking_id, Constants.API_Notification_Type.PATIENT.DOCTOR_DISCONNECTED_CONSULTING);
             *                         }
             *                         else
             *                         {
             *                             dsResult.Tables[2].Rows.Add(model.booking_id, Constants.API_Notification_Type.PATIENT.DOCTOR_CANCELED_CONSULTING);
             *                         }
             *                         string json = JsonConvert.SerializeObject(dt);
             *                     }
             *                     #endregion
             *
             *                     //#region Push Notification For Patient is consulting
             *                     if (model.doctor_manual)
             *                     {
             *                         GetDeviceToPush(model.booking_id, account_id, (int)AccountType.Member, Constants.API_Notification_Type.PATIENT.DOCTOR_DISCONNECTED_CONSULTING);
             *                     }
             *                     else
             *                     {
             *                         GetDeviceToPush(model.booking_id, account_id, (int)AccountType.Member, Constants.API_Notification_Type.PATIENT.DOCTOR_CANCELED_CONSULTING);
             *                     }
             *                     //#endregion
             *                 });
             *                 thread.IsBackground = true;
             *                 thread.Start();
             *                 #endregion
             */
        }

        /**
         * todo 最后还需要：
         *  member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.cancelconsultation", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *  booking_bl.saveLog(model.booking_id, account_id,"", JsonConvert.SerializeObject(model), "CancelConsultation").ConfigureAwait(false);
         */
        return ResponseResultApp.success(errorStr);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp cancelOfflineConsultation(CancelOfflineConsultationDto dto) {
        //todo 进来就先操作了日志：booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "CancelOfflineConsultation").ConfigureAwait(false);
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }

        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }
        //获取展示位置 - 根据bookingId
        TblBookingQueue bookingQueue = tblBookingQueueMapper.findByBookingId(booking.getId());
        tblBookingQueueMapper.deleteByPrimaryKey(booking.getId());
        TblBookingFee bookingFee = tblBookingFeeMapper.findByBookingId(booking.getId());
        BigDecimal zero = new BigDecimal(0);
        bookingFee.setDeliveryFee(zero);
        bookingFee.setPrescriptionFee(zero);
        bookingFee.setConsultationFee(zero);
        bookingFee.setTotalAmount(zero);
        tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);

        //Import Medication In Inventory
        //todo Exec [dbo].[sp_UpdateMedicationInventory] @PRM_booking_id, 2

        tblBookingPrescriptionMapper.updateDelFlagTrueByBookingId(booking.getId());

        //Insert Booking Log
        //Exec [dbo].[sp_UpdateBookingStatus] @PRM_booking_id, -1, 0 --> booking_status: Cancel by customer
        booking.setStatus(-1);
        booking.setSubStatus(0);
//        tblBookingMapper.updateByPrimaryKeySelective(booking);
        /**
         * todo 日志
         * 	Select TOP(1) @driver_id = driver_id
         * 	From tbl_Booking WITH(NOLOCK)
         * 	Where booking_id = @PRM_booking_id
         *
         * 	Exec [dbo].[API_Insert_Booking_Log] @PRM_booking_id, @PRM_booking_status, @PRM_booking_sub_status, @driver_id
         */

        if (CoreAppConstants.IS_BOOKING_QUEUE_ENABLED == 0) {
            doctorConsultStageService.UpdateQueueNoAfterConnected(booking, -5);
        }
        TblDoctor doctor = tblDoctorMapper.selectByPrimaryKey(booking.getDoctorId());
        //Update tbl_Booking Of Doctor
        TblSpecialisation specialisation = tblSpecialisationMapper.selectByPrimaryKey(booking.getSpecialisationId());
        tblBookingQueueMapper.updateQueueNoByGroupIdAndByCountryIdAndByIdentification(doctor.getGroupId(), doctor.getCountryOfResidence(), specialisation.getIdentification());
        /**
         * 这里有个方法，但是方法里面是直接return的，所以不写
         *  await new SQSHelper().UpdateQueueNoAfterConnectedAsync(new UpdateQueueNoAfterConnectedMessage
         *  {
         *      BookingId = booking_id,
         *      DeleteFrom = (int)BookingQueueMessageDeleteFrom.DoctorCancelBookingIsNotManual
         *  });
         */

        return ResponseResultApp.success("Your consultation has been cancelled.");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp skipConsultation(SkipConsultationDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }

        Double timeOutSkipBooking = fnGetConfigTimeOutSkipBooking();
//        if (-1 == booking.getStatus() && 0 == booking.getSubStatus()) {
//            return ResponseResultApp.success();
//        }
        TblDoctor doctor = tblDoctorMapper.selectByPrimaryKey(booking.getDoctorId());
        Date requestOn = tblBookingLogMapper.findOneBlLogDateByBookingId(booking.getId());
        Date calculatedTime = new Date(requestOn.getTime() + ((long) (timeOutSkipBooking * 60) * 1000));
        if (1 != booking.getStatus() || 2 != booking.getStatus() || 3 != booking.getStatus()) {
            return ResponseResultApp.error(doctorConsultStageService.getErrorBookingValid(booking.getStatus()));
        } else if (3 == booking.getStatus() && 0 != booking.getSubStatus()) {
            return ResponseResultApp.error("Sorry, this call ended.");
        } else if (calculatedTime.after(new Date())) {
            return ResponseResultApp.error("Please try again after " + Kit.formatDate(requestOn, "yyyy-MM-dd HH:mm:ss") + " minute(s) !");
        }
        Double timeOutConsulation = fnGetConfigTimeOutCosulation();
        TblSpecialisation specialisation = tblSpecialisationMapper.selectByPrimaryKey(booking.getSpecialisationId());
        TblBookingQueue bookingQueue = tblBookingQueueMapper.selectByPrimaryKey(booking.getId());
        if (ObjectUtil.isNotEmpty(bookingQueue.getSelectedDoctorId())) {
            doctor = tblDoctorMapper.selectByPrimaryKey(bookingQueue.getSelectedDoctorId());
        }
        if (1 == booking.getStatus() || 2 == booking.getStatus() || 3 == booking.getStatus()) {
            //Update Queue No.
//            tblBookingQueueMapper.deleteByPrimaryKey(booking.getId());//这个应该移入临时队列
            tblBookingQueueMapper.updateQueueNoByGroupIdAndByCountryIdAndByIdentification(doctor.getGroupId(), doctor.getCountryOfResidence(), specialisation.getIdentification());
        }
        //就是一个count统计
//            Integer bookingQueueNo = fnGetTotalBookingProcessing(doctor.getId(), "doctor");
        APIBookingUpdateCancellationReason(doctor.getId(), booking.getId(), dto.getCancellation_reason(), null, 2);
        //是否为sp医生
        Boolean isSP = doctor.getPanelType() == 1;
        //这是最大queue_no
        Integer maxPosition = tblBookingQueueMapper.findMaxPositionNo(doctor.getGroupId(), doctor.getId(), doctor.getCountryOfResidence(), specialisation.getIdentification());
        Integer minPosition = 0;
        //todo EXISTS (SELECT 1 FROM tbl_Member_Vip (nolock) WHERE Member_Id = @member_id) 后续等待表 在2.1中
        Boolean isVip = false;
        if (!isSP && ObjectUtil.isNotEmpty("EXISTS (SELECT 1 FROM tbl_Member_Vip (nolock) WHERE Member_Id = @member_id)")) {
            isVip = true;
        }

        if (isVip) {
            /**
             * todo 这里涉及到VIP - 需要通过mem_id连接vip表
             * begin
             * 		SELECT @vip_position = MAX(Case when ISNULL(is_vip,0) = 1 then queue_position end),
             *                @min_position = MIN(
             * 		Case when (
             *                @is_SP = 0
             * 				AND is_doctor_SP = 0
             * 			)
             * 			OR
             * 			(
             *                @is_SP = 1
             * 				AND is_doctor_SP = 1
             * 				AND selected_doctor_id = @PRM_account_id
             * 			)
             * 			then queue_position
             * 			end
             * 		)
             * 		FROM tbl_Booking_Queue  WITH(NOLOCK)
             * 		WHERE is_departed = 0 AND status >= 0
             * 			  and  booking_id!= @PRM_booking_id
             *
             * 		SET @vip_position = ISNULL(@vip_position, 0)
             *
             * 		if (@vip_position > 0)
             * 		begin
             * 			set @vip_position = @vip_position  + 1
             * 		end
             * 		else
             * 		begin
             * 			set @vip_position = ISNULL(@min_position,0)+1
             * 		end
             */
        }
        //将实时位置修改为当前分组-国家-服务-最大值+1
        tblBookingQueueMapper.updateQueueNoByBookingIdAndByMaxQueueNo(booking.getId(), maxPosition + 1);
        if (isVip) {
            /**
             * todo vip
             * begin
             * -- swap position
             * exec dbo.CMS_MovePosition @booking_queue_id,@vip_position
             * end
             */
        }

        if (ObjectUtil.isEmpty(bookingQueue.getSelectedDoctorId())) {
            //如果未选择医生
            booking.setDoctorId(null);
            //这个方法会更新null值
            tblBookingMapper.updateByPrimaryKey(booking);
        }

        Integer positionShowForPatient = 0;
        if (isSP) {
            positionShowForPatient = tblBookingQueueMapper.fnGetNumPatientInFrontOfForSP(doctor.getId(), booking.getId(), doctor.getGroupId(), doctor.getCountryOfResidence(), specialisation.getIdentification(), 1);
            if (CoreAppConstants.IS_BOOKING_QUEUE_ENABLED == 0) {
                Example queueExample = new Example(TblBookingQueue.class);
                queueExample.createCriteria().andEqualTo("selectedDoctorId", doctor.getId()).andEqualTo("delFlag", 0);
                List<TblBookingQueue> queues = tblBookingQueueMapper.selectByExample(queueExample);
                for (TblBookingQueue queue : queues) {
                    Integer i = tblBookingQueueMapper.fnGetNumPatientInFrontOfForSP(doctor.getId(), queue.getId(), doctor.getGroupId(), doctor.getCountryOfResidence(), specialisation.getIdentification(), 0);
                    if (queue.getPositionNo() > 0 && i < queue.getPositionNo()) {
                        queue.setPositionNo(i);
                        tblBookingQueueMapper.updateByPrimaryKeySelective(queue);
                    }

                    TblPushQueueChange pushQueueChange = new TblPushQueueChange();
                    pushQueueChange.setBookingId(booking.getId());
                    pushQueueChange.setQueuePosition(queue.getQueueNo());
                    pushQueueChange.setQueuePositionFake(queue.getPositionNo());
                    tblPushQueueChangeMapper.insertSelective(pushQueueChange);
                }
            }
        } else {
            if (ObjectUtil.isEmpty(bookingQueue.getSelectedDoctorId())) {
                //todo 该317行  SET @position_show_for_patient = dbo.fn_Get_Num_Patient_InFrontOf_For_NotSelect(@booking_queue_id, 1)
            }
        }


        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp cancelBookingAppointment(CancelBookingAppointmentDto dto) {
        //todo 进来先保存了个日志：booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "CancelBookingAppointment").ConfigureAwait(false);
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorised request.");
        }
        TblBookingQueue bookingQueue = tblBookingQueueMapper.selectByPrimaryKey(booking.getId());
        Integer bookingQueueNo = ObjectUtil.isNotEmpty(bookingQueue.getQueueNo()) ? bookingQueue.getQueueNo() : 0;
        Date requestOn = tblBookingLogMapper.findOneBlLogDateByBookingId(booking.getId());
        SysCode timeOutCancelBooking = SysCodeUtil.getCode("Time_Out_Cancel_Booking", "2");
        Double aDouble = ObjectUtil.isNotEmpty(timeOutCancelBooking.getValue()) ? Double.valueOf(timeOutCancelBooking.getValue()) : Double.valueOf(0);
        Date calculatedTime = new Date(requestOn.getTime() + ((long) (aDouble * 60) * 1000));
        if (calculatedTime.after(new Date())) {
            return ResponseResultApp.error("Please try again after " + Kit.formatDate(requestOn, "yyyy-MM-dd HH:mm:ss") + " minute(s) !");
        }
        Double timeOutConsulation = fnGetConfigTimeOutCosulation();
        //Update tbl_Booking Of Patient Current
        TblBookingFee bookingFee = tblBookingFeeMapper.findByBookingId(booking.getId());
        BigDecimal zero = new BigDecimal(0);
        bookingFee.setStandardFee(zero);
        bookingFee.setDeliveryFee(zero);
        bookingFee.setPrescriptionFee(zero);
        bookingFee.setConsultationFee(zero);
        bookingFee.setTotalAmount(zero);
        if (1 == booking.getIsAppointment()) {
            booking.setAppointmentStatus(3);
        }
        APIBookingUpdateCancellationReason(doctor.getId(), booking.getId(), dto.getCancellation_reason(), null, 2);

        //Update tbl_Booking Of Doctor
        /**
         * Begin --- Update tbl_Booking Of Doctor ---
         * 			Update tbl_Booking
         * 			Set booking_queue_no = booking_queue_no - 1,
         * 			update_on = [dbo].[fn_Add_Hours]()
         * 			Where doctor_id = @PRM_account_id
         * 			And ISNULL(booking_queue_no, 0) > 1
         * 		End
         */
//        booking.setStatus(100);
//        booking.setSubStatus(-5);

        //Update Booking Status And Insert Booking Log
        /**
         * todo 以下if和else中最下方都需要这个日志
         * Select TOP(1) @driver_id = driver_id
         * 	From tbl_Booking WITH(NOLOCK)
         * 	Where booking_id = @PRM_booking_id
         *
         * 	Exec [dbo].[API_Insert_Booking_Log] @PRM_booking_id, @PRM_booking_status, @PRM_booking_sub_status, @driver_id
         */
        if (2 == booking.getStatus() && 0 == booking.getSubStatus()){
            booking.setStatus(-1);
            booking.setSubStatus(-2);
        }else {
            booking.setStatus(-1);
            booking.setSubStatus(-1);
        }
        if (CoreAppConstants.IS_BOOKING_QUEUE_ENABLED == 0){
            doctorConsultStageService.UpdateQueueNoAfterConnected(booking,-5);
            /**
             * todo 这里有一个这个函数
             * ALTER PROCEDURE [dbo].[DIS_Recalculate_Inventory_After_Consult]
             *        @PRM_booking_id uniqueidentifier
             * AS
             * BEGIN
             * 	Declare @medication_id varchar(50)
             * 	DECLARE Prescription_Not_Buy CURSOR FOR
             * 	Select Distinct MDMedication_ID
             * 		From tbl_Booking_Prescription with (nolock)
             * 		Where booking_id = @PRM_booking_id And prescription_status In(0,1)
             * 	OPEN Prescription_Not_Buy
             * 	FETCH NEXT FROM Prescription_Not_Buy INTO @medication_id
             * 	WHILE @@FETCH_STATUS = 0
             * 		BEGIN
             * 		     Exec DIS_Recalculate_And_Update_Quantity @medication_id
             * 			FETCH NEXT FROM Prescription_Not_Buy INTO @medication_id;
             * 		END;
             * 	CLOSE Prescription_Not_Buy;
             * 	DEALLOCATE Prescription_Not_Buy;
             * END
             */
        }
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);
        String message = "Your consultation has been cancelled.";
        ResponseResultApp bookingInfo = patientHistoryService.getBookingInfo(booking.getId());
        /**
         * 这里还有一句，但是进入对方方法查看，目前来说是直接return的，也就是没有任何操作。
         * await new SQSHelper().UpdateQueueNoAfterConnectedAsync(new UpdateQueueNoAfterConnectedMessage
         *                 {
         *                     BookingId = model.booking_id,
         *                     DeleteFrom = (int)BookingQueueMessageDeleteFrom.DoctorCancelBookingIsNotManual
         *                 });
         */
        //todo 发送消息GetDeviceToPush(model.booking_id, account_id, (int)AccountType.Member, Constants.API_Notification_Type.PATIENT.DOCTOR_SKIPPED_APPOINTMENT, "", 1, str_appointment_time);
        BookingInfoVo vo = (BookingInfoVo)bookingInfo.getData();
        return ResponseResultApp.success(message,vo);
    }

    /**
     * 函数： fn_Get_TotalBookingProcessing
     *
     * @param accountId   账号
     * @param accountType 账号类型字符串：例如：doctor,member,child,doctor_no_consult,patient_on_queue
     * @return
     */
    public Integer fnGetTotalBookingProcessing(String accountId, String accountType) {
        if (ObjectUtil.isEmpty(accountId) || ObjectUtil.isEmpty(accountType)) {
            return 0;
        }
        if (accountType.equals("doctor")) {
            return tblBookingMapper.findCountByDoctorIdAndByStatus(accountId);
        }
        return 0;
    }

    /**
     * 函数：fn_GetConfig_TimeOutCosulation
     *
     * @return
     */
    public Double fnGetConfigTimeOutCosulation() {
        SysCode code = SysCodeUtil.getCode("Time_Out_Cosulation", "2");
        if (ObjectUtil.isEmpty(code.getValue())) {
            return (double) 1;
        }
        return Double.valueOf(code.getValue());
    }

    /**
     * 函数： fn_GetConfig_TimeOutSkipBooking
     *
     * @return
     */
    public Double fnGetConfigTimeOutSkipBooking() {
        SysCode code = SysCodeUtil.getCode("Time_Out_Skip_Booking", "2");
        if (ObjectUtil.isEmpty(code.getValue())) {
            return (double) 0;
        }
        return Double.valueOf(code.getValue());
    }

    @Transactional(rollbackFor = Exception.class)
    public void DISRecalculateInventoryAfterConsult(String bookingId) {
        /**
         * ALTER PROCEDURE [dbo].[DIS_Recalculate_Inventory_After_Consult]
         *        @PRM_booking_id uniqueidentifier
         * AS
         * BEGIN
         * 	Declare @medication_id varchar(50)
         * 	DECLARE Prescription_Not_Buy CURSOR FOR
         * 	Select Distinct MDMedication_ID
         * 		From tbl_Booking_Prescription with (nolock)
         * 		Where booking_id = @PRM_booking_id And prescription_status In(0,1)
         * 	OPEN Prescription_Not_Buy
         * 	FETCH NEXT FROM Prescription_Not_Buy INTO @medication_id
         * 	WHILE @@FETCH_STATUS = 0
         * 		BEGIN
         * 		     Exec DIS_Recalculate_And_Update_Quantity @medication_id
         * 			FETCH NEXT FROM Prescription_Not_Buy INTO @medication_id;
         * 		END;
         * 	CLOSE Prescription_Not_Buy;
         * 	DEALLOCATE Prescription_Not_Buy;
         * END
         */
    }


    /**
     * 函数： API_DoctorUpdateDiagnosis
     *
     * @param bookingId                bookingId
     * @param diagnosisList            诊断集合
     * @param doctorCountryOfResidence 医生所在国家
     */
    @Transactional(rollbackFor = Exception.class)
    public void APIDoctorUpdateDiagnosis(String bookingId, List<CancelConsultationDto.Diagnosis> diagnosisList, String doctorCountryOfResidence, String groupId) {
        //查询当前group的诊断最大order并且sql中+1了
        Integer order = tblDiagnosisMapper.findMaxDispOrderAddOneByGroupId(groupId);
        //软删除
        tblBookingDiagnosisMapper.updateDelFlagTrueByBookingId(bookingId);
        for (CancelConsultationDto.Diagnosis item : diagnosisList) {
            TblDiagnosis byNameOrCode = tblDiagnosisMapper.findByGroupIdAndByNameOrCode(groupId, item.getName());
            //如果这个名称未查询出来 - 则保存
            if (ObjectUtil.isEmpty(byNameOrCode)) {
                TblDiagnosis diagnosis = new TblDiagnosis();
                diagnosis.setCode(item.getCode() != null ? item.getCode() : null);
                diagnosis.setName(item.getName());
                diagnosis.setStatus(1);
                diagnosis.setDispOrder(order);
                diagnosis.setCountryOfResidence(doctorCountryOfResidence);
                order++;
                tblDiagnosisMapper.insertSelective(diagnosis);
            }
            TblDiagnosis diagnosis = tblDiagnosisMapper.findOneByNameAndByGroupIdAndByCountryId(item.getName(), groupId, doctorCountryOfResidence);
            if (ObjectUtil.isNotEmpty(diagnosis)) {
                TblBookingDiagnosis bookingDiagnosis = new TblBookingDiagnosis();
                bookingDiagnosis.setBookingId(bookingId);
                bookingDiagnosis.setDiagnosisId(item.getId());
                bookingDiagnosis.setInExclusionList(item.getIs_in_exclusion_list() ? 1 : 0);
                tblBookingDiagnosisMapper.insertSelective(bookingDiagnosis);
            }
        }

        //保存日志
        APIDoctorUpdateDiagnosisLog(bookingId, diagnosisList, 0);

    }

    /**
     * 函数： API_Doctor_UpdateDiagnosisLog
     *
     * @param bookingId     bookingId
     * @param diagnosisList 存储当前所有诊断的json数组[{diagnosis_nam:code - name,is_in_exclusion_list:true},{},…]
     * @param repeat        医生端点击病人历史，勾选中历史的diagnosis并且添加，添加一条历史的booking的diagnosis_log记录，结构为:[{"type":1,"Diagnosis_ID":9160}],感觉用不上，保留
     */
    @Transactional(rollbackFor = Exception.class)
    public void APIDoctorUpdateDiagnosisLog(String bookingId, List<CancelConsultationDto.Diagnosis> diagnosisList, Integer repeat) {
        // 创建一个ObjectMapper对象
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String json = objectMapper.writeValueAsString(diagnosisList);
            TblBookingDiagnosisLog bookingDiagnosisLog = new TblBookingDiagnosisLog();
            bookingDiagnosisLog.setBookingId(bookingId);
            bookingDiagnosisLog.setJson(json);
            bookingDiagnosisLog.setRepeat(repeat);
            tblBookingDiagnosisLogMapper.insertSelective(bookingDiagnosisLog);
        } catch (JsonProcessingException e) {
            log.error("保存诊断时转换诊断JSON错误，bookingId为：{}", bookingId);
        }
    }

    /**
     * 函数 API_BookingUpdateCancellationReason
     *
     * @param accountId                账号ID
     * @param bookingId                bookingId
     * @param cancellationReason       取消原因
     * @param cancellationReasonDetail 取消原因详细信息
     * @param accountType              账号类型：1-member，2-doctor
     */
    @Transactional(rollbackFor = Exception.class)
    public void APIBookingUpdateCancellationReason(String accountId, String bookingId, String cancellationReason,
                                                   String cancellationReasonDetail, Integer accountType) {
        //必须传入原因才操作
        if (ObjectUtil.isNotEmpty(cancellationReason)) {
            //根据booking查询预约取消原因表 - 一对一
            TblBookingCancellationReason bookingCancellationReason = tblBookingCancellationReasonMapper.findByBookingId(bookingId);
            if (1 == accountType) {
                //member
                if (ObjectUtil.isNotEmpty(bookingCancellationReason)) {
                    bookingCancellationReason.setMemberId(accountId);
                    bookingCancellationReason.setMemberCancellationReason(cancellationReason);
                    bookingCancellationReason.setMemberCancellationReasonDetail(cancellationReasonDetail);
                    tblBookingCancellationReasonMapper.updateByPrimaryKeySelective(bookingCancellationReason);
                } else {
                    bookingCancellationReason = new TblBookingCancellationReason();
                    bookingCancellationReason.setBookingId(bookingId);
                    bookingCancellationReason.setMemberId(accountId);
                    bookingCancellationReason.setMemberCancellationReason(cancellationReason);
                    bookingCancellationReason.setMemberCancellationReasonDetail(cancellationReasonDetail);
                    tblBookingCancellationReasonMapper.insertSelective(bookingCancellationReason);
                }
            } else if (2 == accountType) {
                //doctor
                if (ObjectUtil.isNotEmpty(bookingCancellationReason)) {
                    bookingCancellationReason.setDoctorId(accountId);
                    bookingCancellationReason.setDoctorCancellationReason(cancellationReason);
                    bookingCancellationReason.setDoctorCancellationReasonDetail(cancellationReasonDetail);
                    tblBookingCancellationReasonMapper.updateByPrimaryKeySelective(bookingCancellationReason);
                } else {
                    bookingCancellationReason = new TblBookingCancellationReason();
                    bookingCancellationReason.setBookingId(bookingId);
                    bookingCancellationReason.setDoctorId(accountId);
                    bookingCancellationReason.setDoctorCancellationReason(cancellationReason);
                    bookingCancellationReason.setDoctorCancellationReasonDetail(cancellationReasonDetail);
                    tblBookingCancellationReasonMapper.insertSelective(bookingCancellationReason);
                }
            }
        }
    }

}
